/*
 * \file CplexColorSolver.cpp
 * \brief Implementación de la clase CplexColorSolver
 *
 *  Created on: Jul 29, 2014
 *      Author: correa
 */

#include <CplexColorSolver.h>
#include <Separator.h>
#include <SeparatorParameters.h>
#include <RDCliquer.h>
#include <HeuristicasGolosas.h>
#include <ColorBranching.h>
#include <RepSearchTree.h>
#include <Reserva.h>
#include <TimeLimit.h>
#include <string>
#include <list>

DoubleValueArray* CplexColorSolver::solve(SeparatorParameters& parameters)
{
	DoubleValueArray * ret = 0;
	try
	{
		IloEnv env;
		IloModel model(env);
		CplexStabColorModel cplex(model, _grafo, _k, _mapa, _array, parameters);

		int n = _grafo.getN();
		VertexSet vset(n);
		IloRangeArray constraints(env);
		IloNumVarArray& x = cplex.getX();
		IloNumVarArray& y = cplex.getY();

		// russian dolls method
		DiGrafo order(_grafo.getN());
		order.addAllVertices();

		// clique maxima con cobertura por conjuntos independientes
		vset.addAll();
		VertexSet clique(n);



		list<Subproblema *> subpactivos;
		subpactivos.clear();

		int cm;
		int bestObjValue = 0;
		do
		{
			clique.removeAll();
			cm = cliqueMaximal(&vset, &clique);
			cout << "clique maximal: ";
			clique.print();

			if (cm >= 2)
			{
				Subproblema * subproblem = new Subproblema(env, _grafo, vset, order, _array, x.getSize());

				constraints.clear();

				for (VertexSetIterator itv = clique.begin(); subproblem->ncolores() < _k && itv != clique.end(); ++itv) {
					subproblem->addCandidato(*itv, constraints, x, y);
					subproblem->extendWithStab(*itv, constraints, x, y);
				}

				if (bestObjValue < subproblem->tamanoMuneca()-subproblem->ncolores())
					bestObjValue = subproblem->tamanoMuneca()-subproblem->ncolores();
				if (bestObjValue < subproblem->tamano())
				{
					// desigualdad de clique para la clique maxima
					if (cm > 2)
						subproblem->addCliqueConst(clique, constraints, x);
					model.add(constraints);

					subpactivos.push_back(subproblem);
					vset.removeAll(clique);
				}
				else
					cm = 0;
			}
		}
		while (cm >= 2);


		int mipStart;
		int bound;
		while (!subpactivos.empty())
		{
			for (int r = 0; r < n; ++r)
				cplex.separator(r)->getReservaCliques()->haceActiva();

			Subproblema * subproblem_pt = subpactivos.back();
			Subproblema subproblem = *subproblem_pt;
			subpactivos.pop_back();
			subproblem.activarTodos(x, y);

			subproblem.print(y);

			subproblem.buscarYDesactivar(x, y);
			int objValue = subproblem.tamanoMuneca()-subproblem.ncolores();
			bound = objValue;

			subproblem.print(y);

			while (subproblem.tamanoMuneca() < subproblem.tamano()) {
				constraints.clear();
				int newv = subproblem.addCandidato(constraints, x, y);

				// desigualdad que impone numero cromatico
				++bound;
				subproblem.addChromoBoundConst(bound, constraints, x, y);
				model.add(constraints);

				//				cplex.exportModel("color.lp");

				subproblem.print(y);

				// Optimize the problem and obtain solution.
				if (subproblem.ncolores() >= 2) {
					int prevObjValue = objValue;

					bool hasnsc = false;
					IloRange nosingconst(env, 0, 0);
					if (bound > bestObjValue) // || bound-objValue >= 1)
					{
						subproblem.createNosingletonConst(newv, nosingconst, x);
						model.add(nosingconst);
						hasnsc = true;

						constraints.clear();
						subproblem.addNosymConsts(newv, constraints, x);
						model.add(constraints);

						if (cplex.getNMIPStarts() == 0)
							mipStart = cplex.addMIPStart(x, subproblem.solucionEntera());
						else
							cplex.changeMIPStart(mipStart, x, subproblem.solucionEntera());
						int status = cplex.solve();
						IloAlgorithm::Status probstat = cplex.getStatus();
						if (!status && (probstat != IloAlgorithm::Status::Infeasible))
						{
							env.error() << "No se pudo resolver el modelo. " << status << endl;
							throw(-1);
						}
						env.out() << "Solution status = " << probstat << endl;

						if (probstat == IloAlgorithm::Status::Optimal)
							objValue = cplex.getObjValue();

						if (bound > objValue)
						{
							cout << "#################################################bound=" << bound << " objval=" << objValue << " bestobj=" << bestObjValue << endl;
//							exit(0);
						}
						bound = objValue;
					}

					// greedify
					env.out() << "Objective value  = " << objValue << endl;
					env.out() << "Best objective value  = " << bestObjValue << endl;
					env.out() << "Bound  = " << bound << endl;
					if (prevObjValue == objValue && subproblem.ncolores() < _k)
					{
						constraints.clear();

						int agregados = subproblem.extendWithStab(newv, constraints, x, y);
						objValue += agregados;
						bound += agregados;
						cout << "agregados con extensión (newv=" << newv << "): " << agregados << endl;
						subproblem.buscarYDesactivar(x, y);

						if (subproblem.tamanoMuneca() < subproblem.tamano() || !subpactivos.empty())
						{
							// desigualdad que impone numero cromatico
							subproblem.addChromoBoundConst(bound, constraints, x, y);
							subproblem.addChromoConst(bound, constraints, x);
						}
						model.add(constraints);
					}
					else if (hasnsc)
					{
						int agregados = subproblem.assignx(cplex, x, y);
						objValue += agregados;
						bound = objValue;
						cout << "agregados con recolor: " << agregados << endl;
					}
					env.out() << "Solution value  = " << subproblem.tamanoMuneca() << endl;
					env.out() << "Colors  = " << subproblem.ncolores() << endl;

					if (objValue > bestObjValue)
						bestObjValue = objValue;
					if (hasnsc)
						model.remove(nosingconst);
					subproblem.print(y);

//					if (probstat == IloAlgorithm::Status::Infeasible)
//						exit(0);
				}
			}
//			delete subproblem_pt;
		}

		env.end();
	} catch (IloAlgorithm::NotExtractedException& e)
	{
		cerr << "Not extractated exception caught: " << e << endl;
		cerr << e.getExtractable().getName() << endl;
	} catch (IloException& e)
	{
		cerr << "Concert exception caught: " << e << endl;
	} catch (...)
	{
		cerr << "Unknown exception caught" << endl;
	}

	return ret;
}

DoubleValueArray* CplexColorSolver::solveLinearRelaxation()
{
	DoubleValueArray * ret = 0;
	IloEnv env;
	try
	{
		IloModel model(env);

		IloNumVarArray var(env);
		IloRangeArray con(env);

		int n = _grafo.getN();

		IloEnv env = model.getEnv();

		//variable definition
		IloNumVarArray & x = *(new IloNumVarArray(env));
		for (int r = 0; r < n; ++r) {
			x.add(IloNumVar(env, 0, 1));				// x^r_r
			for (int u = 0; u < _mapa[r].size(); u++)
				x.add(IloNumVar(env, 0, 1));			// x^r_u
		}

		//objective function
		IloNumExpr fobj(env);

		//constraints
		IloRangeArray constraints(env);
		VertexSet mask(n);
		VertexSet novecinos(n);
		for (int r = 0; r < n; ++r) {
			IloRange coverconst(env, 1, 1);
			fobj += x[_array[r][r]];
			coverconst.setLinearCoef(x[_array[r][r]], 1);

			novecinos.copyCompl(_grafo.vecinos(r));

			for (VertexSetIterator v = novecinos.begin(); v != novecinos.end() && *v < r; ++v)
				coverconst.setLinearCoef(x[_array[*v][r]], 1);

			mask.add(r);
			novecinos.removeAll(mask);
			VertexSet aristas(n);
			for (VertexSetIterator v = novecinos.begin(); v != novecinos.end(); ++v) {
				aristas.copyAll(novecinos);
				aristas.retainAll(_grafo.vecinos(*v));
				VertexSetIterator u = aristas.begin();
				if (u == aristas.end())
					constraints.add(x[_array[r][*v]] + x[_array[r][r]] <= 1);
				else for (; u != aristas.end() && *u < *v; ++u)
					constraints.add(x[_array[r][*u]] + x[_array[r][*v]] + x[_array[r][r]] <= 1);
			}
			constraints.add(coverconst);
		}
		model.add(constraints);
		model.add(IloMinimize(env, fobj));

		IloCplex cplex(model);

		// Optimize the problem and obtain solution.
		if (!cplex.solve())
		{
			env.error() << "No se pudo resolver el modelo." << endl;
			throw(-1);
		}

		env.out() << "Valor relajación lineal  = " << cplex.getObjValue() << endl;

		for (int i = 0; i < n; i++)
			env.out() << "x[" << i << "]=" << cplex.getValue(x[i]) << endl;

	} catch (IloException& e)
	{
		cerr << "Concert exception caught: " << e << endl;
	} catch (...)
	{
		cerr << "Unknown exception caught" << endl;
	}

	env.end();

	return ret;
}
